// -----------------------------------------------------------------------------
// This source file is part of Matrix Platform
// 	(Universal .NET Software Development Platform)
// For the latest info, see http://www.matrixplatform.com
// 
// Copyright (c) 2009-2010, Ingenious Ltd
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Collections.ObjectModel;
using Matrix.Common.Core;

namespace Matrix.Common.Diagnostics.TracerCore.Items
{
    /// <summary>
    /// Base class for Tracer Items. Each item represents a call to the tracer
    /// system to record a given event. Items are processed, filtered, "sinked"
    /// into other destinations etc.
    /// </summary>
    [Serializable]
    public class TracerItem
    {
        /// <summary>
        /// An item can only have one of these values assigned, but some 
        /// external component may choose to do a combination for some purpose.
        /// </summary>
        public enum TypeEnum
        {
            Debug = 1,
            Info = 2,
            Warning = 4,
            Error = 8,
            Fatal = 16,
        }

        public enum ExtendedTypeEnum
        {
            None = 0,
            MethodEntry = 1,
            MethodExit = 2,
            Operation = 4
        }

        /// <summary>
        /// Non-combinationary, each tracer item has an assigned priority.
        /// </summary>
        public enum PriorityEnum : byte
        {
            Trivial = 1,
            Minimum = Trivial,
            VeryLow = 3,
            Low = 10,
            //MediumLow = 15,
            Medium = 20,
            Default = Medium,
            //MediumHigh = 25,
            High = 30,
            VeryHigh = 50,
            Critical = 100
        }

        volatile PriorityEnum _priority = PriorityEnum.Medium;
        public PriorityEnum Priority
        {
            get { return _priority; }
            set { _priority = value; }
        }

        volatile ExtendedTypeEnum _extendedType = ExtendedTypeEnum.None;
        /// <summary>
        /// 
        /// </summary>
        public ExtendedTypeEnum ExtendedType
        {
            get { return _extendedType; }
            set { _extendedType = value; }
        }

        TypeEnum _type;
        /// <summary>
        /// May contain multiple values.
        /// </summary>
        public TypeEnum Type
        {
            get { return _type; }
        }

        DateTime _dateTime;
        public DateTime DateTime
        {
            get { return _dateTime; }
        }

        Exception _exception;
        
        /// <summary>
        /// Optional exception value.
        /// </summary>
        public Exception Exception
        {
            get { return _exception; }
            internal set { _exception = value; }
        }

        string _sourceId = string.Empty;
        /// <summary>
        /// Source is assigned when a trace item has been received from a remote source.
        /// For local sources, the value is String.Empty.
        /// </summary>
        public string SourceId
        {
            get { return _sourceId; }
            set { _sourceId = value; }
        }

        long _applicationTick;
        /// <summary>
        /// The time the item was created, in ticks from start of application.
        /// </summary>
        public long ApplicationTick
        {
            get { return _applicationTick; }
            set { _applicationTick = value; }
        }

        /// <summary>
        /// May be null.
        /// </summary>
        public virtual Assembly DeclaringAssembly
        {
            get { return null; }
        }

        volatile string _message;
        public string Message
        {
            get { return _message; }
        }

        volatile string _source;
        public string Source
        {
            get { return _source; }
        }

        long _index = -1;
        /// <summary>
        /// Index is assigned by tracer.
        /// </summary>
        public long Index
        {
            get { return _index; }
            set { _index = value; }
        }

        [NonSerialized]
        InstanceMonitor _monitor = null;
        /// <summary>
        /// The monitor instance that generated the item.
        /// May be null when no monitor was used, or after 
        /// serialization - deserialization.
        /// </summary>
        public InstanceMonitor InstanceMonitor
        {
            get { return _monitor; }
            set { _monitor = value; }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public TracerItem(TypeEnum itemType, ExtendedTypeEnum extendedType, PriorityEnum priority, string message, string source)
        {
            _priority = priority;
            _type = itemType;
            _extendedType = extendedType;
            _dateTime = DateTime.Now;
            _applicationTick = ApplicationLifetimeHelper.ApplicationStopwatchTicks;
            _message = message;
            _source = source;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public TracerItem(TypeEnum itemType, ExtendedTypeEnum extendedType, DateTime time, PriorityEnum priority, string message, string source)
        {
            _priority = priority;
            _type = itemType;
            _extendedType = extendedType;
            _dateTime = time;
            _applicationTick = ApplicationLifetimeHelper.ApplicationStopwatchTicks;
            _message = message;
            _source = source;
        }

        ///// <summary>
        ///// Attach prints of objects related to this tracer item. This is not mandatory.
        ///// </summary>
        //public void AttachObjectsPrints(ITracerObject[] tracerObjects)
        //{
        //    if (tracerObjects == null || tracerObjects.Length == 0)
        //    {
        //        return;
        //    }

        //    _attachedObjectsPrints = new string[tracerObjects.Length];
        //    for (int i = 0; i < tracerObjects.Length; i++)
        //    {
        //        _attachedObjectsPrints[i] = tracerObjects[i].Print();
        //    }
        //}

        /// <summary>
        /// Obtain the prefix part for a print of an item.
        /// </summary>
        /// <returns></returns>
        string GetPrintPrefix()
        {
            switch (_extendedType)
            {
                case TracerItem.ExtendedTypeEnum.MethodEntry:
                    return ">>" + _type.ToString();
                case TracerItem.ExtendedTypeEnum.MethodExit:
                    return "<<" + _type.ToString();
                default:
                    return "[" + _type.ToString() + "]";
            }
        }

        /// <summary>
        /// Print item information into one line string.
        /// </summary>
        /// <returns></returns>
        public virtual string PrintPrefix(char separator)
        {
            return string.Format("{0}" + separator + "{1}" + separator+ "{2}", _index.ToString("000000"), 
                                 _dateTime.ToString("dd/MM/yyyy HH:mm:ss.FFFFF"/*CoreGeneralHelper.UniversalNumberFormatInfo*/), GetPrintPrefix());
        }

        public virtual string PrintMessage(bool includeOptionalException)
        {
            if (includeOptionalException && _exception != null)
            {
                return CommonHelper.ProcessExceptionMessage(_message, _exception);
            }
            else
            {
                return _message;
            }
        }

        public static TracerItem ParseFileItem(string line)
        {
            if (string.IsNullOrEmpty(line))
            {
                return null;
            }

            try
            {
                string[] substrings = line.Split('|');

                if (substrings.Length < 4)
                {
                    SystemMonitor.OperationError("Failed to parse tracer item line [" + line + ", Not enough substrings generated].");
                    return null;
                }

                TracerItem.TypeEnum fullType = (TracerItem.TypeEnum)int.Parse(substrings[0]);
                TracerItem.PriorityEnum priority = (TracerItem.PriorityEnum)int.Parse(substrings[1]);

                long index = 0;
                long.TryParse(substrings[2], out index);

                DateTime time;
                try
                {
                    string dateTime = substrings[3];
                    string[] dateTimeParts = dateTime.Split('/');
                    string[] subParts = dateTimeParts[2].Split(' ');
                    TimeSpan timeSpan = TimeSpan.Parse(subParts[1]);

                    time = new DateTime(int.Parse(subParts[0]), int.Parse(dateTimeParts[1]),
                                        int.Parse(dateTimeParts[0]), timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds);
                }
                catch (Exception ex2)
                {
                    SystemMonitor.OperationError("Failed to parse tracer item line [" + line + ", " + ex2.Message + "].");
                    time = DateTime.MinValue;
                }

                return new TracerItem(fullType, ExtendedTypeEnum.None, time, priority, 
                    index.ToString() + "  " + substrings[substrings.Length - 1], string.Empty);
            }
            catch (Exception ex)
            {
                SystemMonitor.OperationError("Failed to parse tracer item line [" + line + ", " + ex.Message + "].");
                return null;
            }
        }

        public virtual string PrintFileLine()
        {
            return ((int)Type).ToString() + '|' + ((int)Priority).ToString() + '|' + PrintPrefix('|') + '|' + PrintMessage(true);
        }


    }
}
